package org.sxp.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.sxp.entity.CodeModel;
import org.sxp.entity.ColumnEntity;
import org.sxp.entity.TableEntity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 代码生成
 * @Author 沈兴平
 * @Date 2020/11/1
 */
public class GenUtils {

	public static List<String> getTemplates(CodeModel codeParams){
		List<String> templates = new ArrayList<String>();
		templates.add("template/Entity.java.vm");
		templates.add("template/Dao.java.vm");
		templates.add("template/MysqlDao.xml.vm");
		templates.add("template/OracleDao.xml.vm");
		templates.add("template/Service.java.vm");
		templates.add("template/ServiceImpl.java.vm");
		templates.add("template/Controller.java.vm");
//		templates.add("template/Bo.java.vm");
//		templates.add("template/BoImpl.java.vm");
		templates.add("template/Dto.java.vm");
		templates.add("template/list.html.vm");
		templates.add("template/list.js.vm");
		templates.add("template/menu.sql.vm");

		if(codeParams.getRpc()) {
			templates.add("template/RpcService.java.vm");
		}
		return templates;
	}
	
	/**
	 * 生成代码
	 */
	public static void generatorCodeToZip(Map<String, String> table,
			List<Map<String, String>> columns, ZipOutputStream zip,CodeModel codeParams){
		TemplateParamsModel model = getTemplateParams(table,columns, codeParams);
		if(StringUtils.isNotEmpty(codeParams.getModuleName())){
			model.setModuleName(codeParams.getModuleName());
		}
        VelocityContext context = new VelocityContext(JSON.parseObject(JSON.toJSONString(model)));
        
        //获取模板列表
		List<String> templates = getTemplates(codeParams);
		for(String template : templates){
			//渲染模板
			StringWriter sw = new StringWriter();
			Template tpl = Velocity.getTemplate(template, "UTF-8");
			tpl.merge(context, sw);

			try {
				//添加到zip
				String fileName = getFileNameByZip(template, model.getClassName(), model.getPackageName(), model.getModuleName());
				if(StringUtils.isNotBlank(fileName)) {
					zip.putNextEntry(new ZipEntry(fileName));
					IOUtils.write(sw.toString(), zip, "UTF-8");
					IOUtils.closeQuietly(sw);
					zip.closeEntry();
				}
			} catch (IOException e) {
				throw new XPException("渲染模板失败，表名：" + model.getTableName(), e);
			}
		}
	}
	
	public static void generatorCodeToProjectDir(Map<String, String> table, List<Map<String, String>> columns, CodeModel codeParams){
		TemplateParamsModel model = getTemplateParams(table,columns,codeParams);
		if(StringUtils.isNotEmpty(codeParams.getModuleName())){
			model.setModuleName(codeParams.getModuleName());
		}
		VelocityContext context = new VelocityContext(JSON.parseObject(JSON.toJSONString(model)));

		//获取模板列表
		List<String> templates = getTemplates(codeParams);
		for(String template : templates){
			//渲染模板
			StringWriter sw = new StringWriter();
			Template tpl = Velocity.getTemplate(template, "UTF-8");
			tpl.merge(context, sw);

			try {
				String fileName = getFileNameByProjectDir(template, model.getClassName(),codeParams);
				if(StringUtils.isNotBlank(fileName)){
					FileOutputStream output = new FileOutputStream(fileName);
					IOUtils.write(sw.toString(), output, "UTF-8");
					IOUtils.closeQuietly(sw);
					output.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new XPException("渲染模板失败，表名：" + model.getTableName(), e);
			}
		}
	}

	private static TemplateParamsModel getTemplateParams(Map<String, String> table,List<Map<String, String>> columns, CodeModel codeParams){
		//配置信息
		Configuration config = getConfig();
		boolean hasBigDecimal = false;
		//表信息
		TableEntity tableEntity = new TableEntity();
		tableEntity.setTableName(table.get("tableName"));
		tableEntity.setComments(table.get("tableComment"));
		//表名转换成Java类名
		String className = tableToJava(tableEntity.getTableName(), codeParams);
		tableEntity.setClassName(className);
		tableEntity.setClassname(StringUtils.uncapitalize(className));

		//列信息
		List<ColumnEntity> columsList = new ArrayList<>();
		for(Map<String, String> column : columns){
			ColumnEntity columnEntity = new ColumnEntity();
			columnEntity.setColumnName(column.get("columnName"));
			columnEntity.setDataType(column.get("dataType"));
			columnEntity.setComments(column.get("columnComment"));
			columnEntity.setExtra(column.get("extra"));
			Object maxLength = column.get("maxLength");
			String  ss= maxLength == null ? "" : maxLength.toString();
			columnEntity.setMaxLength(ss);

			//列名转换成Java属性名
			String attrName = columnToJava(columnEntity.getColumnName());
			columnEntity.setAttrName(attrName);
			columnEntity.setAttrname(StringUtils.uncapitalize(attrName));

			//列的数据类型，转换成Java类型
			String attrType = config.getString(columnEntity.getDataType(), "unknowType");
			columnEntity.setAttrType(attrType);
			if (!hasBigDecimal && attrType.equals("BigDecimal" )) {
				hasBigDecimal = true;
			}
			//是否主键
			if("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null){
				tableEntity.setPk(columnEntity);
			}

			columsList.add(columnEntity);
		}
		tableEntity.setColumns(columsList);

		//没主键，则第一个字段为主键
		if(tableEntity.getPk() == null){
			tableEntity.setPk(tableEntity.getColumns().get(0));
		}

		//设置velocity资源加载器
		Properties prop = new Properties();
		prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		Velocity.init(prop);

		String mainPath = config.getString("mainPath" );
		mainPath = StringUtils.isBlank(mainPath) ? "org.sxp" : mainPath;

		//封装模板数据
		TemplateParamsModel model = new TemplateParamsModel();
		model.setTableName(tableEntity.getTableName());
		model.setComments(tableEntity.getComments());
		model.setPk(tableEntity.getPk());
		model.setClassName(tableEntity.getClassName());
		model.setClassname(tableEntity.getClassname());
		model.setPathName(tableEntity.getClassname().toLowerCase());
		model.setColumns(tableEntity.getColumns());
		model.setHasBigDecimal(hasBigDecimal);
		model.setMainPath(mainPath);
		model.setPackageName(config.getString("packageName" ));
		model.setModuleName(config.getString("moduleName" ));
		model.setAuthor(config.getString("author"));
		model.setEmail(config.getString("email"));
		model.setDatetime(DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
		model.setRpc(codeParams.getRpc());
		return model;
	}
	/**
	 * 列名转换成Java属性名
	 */
	public static String columnToJava(String columnName) {
		return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
	}
	
	/**
	 * 表名转换成Java类名
	 */
	public static String tableToJava(String tableName, CodeModel codeParams) {
		if(StringUtils.isNotBlank(codeParams.getTablePrefix())){
			tableName = "&"+tableName;

			tableName = tableName.replace("&"+codeParams.getTablePrefix().toUpperCase(), "");
			tableName = tableName.replace("&"+codeParams.getTablePrefix().toLowerCase(), "");
		}
//		if(tableName.lastIndexOf("_") > 0){
//			tableName = tableName.substring(0, tableName.lastIndexOf("_"));
//		}
		if(StringUtils.isNotBlank(codeParams.getTableSuffix())){
			tableName = tableName+"&";
////
			tableName = tableName.replace(codeParams.getTableSuffix().toUpperCase()+"&", "");
			tableName = tableName.replace(codeParams.getTableSuffix().toLowerCase()+"&", "");
//			tableName = tableName.substring(0, tableName.lastIndexOf("_"));
		}
		tableName = tableName.replace("&", "");
		return columnToJava(tableName);
	}

	/**
	 * 获取配置信息
	 */
	public static Configuration getConfig(){
		try {
			PropertiesConfiguration configuration = new PropertiesConfiguration();
			configuration.setEncoding("UTF8");
			configuration.load("generator.properties");
			return configuration;
		} catch (ConfigurationException e) {
			throw new XPException("获取配置文件失败，", e);
		}
	}

	/**
	 * 获取文件名
	 */
	public static String getFileNameByZip(String template, String className, String packageName, String moduleName) {
		String packagePath = "main" + File.separator + "java" + File.separator;
		if (StringUtils.isNotBlank(packageName)) {
			packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
		}

		if (template.contains("Entity.java.vm" )) {
			return packagePath + "entity" + File.separator + className + "DO.java";
		}

		if (template.contains("Dto.java.vm" )) {
			return packagePath + "dto" + File.separator + className + "DTO.java";
		}

		if (template.contains("Dao.java.vm" )) {
			return packagePath + "dao" + File.separator + className + "DAO.java";
		}


		if (template.contains("RpcService.java.vm" )) {
			return packagePath + "service" + File.separator + "Rpc" + className + "Service.java";
		}

		if (template.contains("Service.java.vm" )) {
			return packagePath + "service" + File.separator + className + "Service.java";
		}

		if (template.contains("ServiceImpl.java.vm" )) {
			return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
		}

		if (template.contains("Bo.java.vm" )) {
			return packagePath + "bo" + File.separator + File.separator + className + "BO.java";
		}

		if (template.contains("BoImpl.java.vm" )) {
			return packagePath + "bo" + File.separator + "impl" + File.separator + className + "BOImpl.java";
		}

		if (template.contains("Controller.java.vm" )) {
			return packagePath + "controller" + File.separator + className + "Controller.java";
		}

		if (template.contains("MysqlDao.xml.vm" )) {
			return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + moduleName + File.separator + className + "Mapper.xml";
		}

//		if (template.contains("OracleDao.xml.vm" )) {
//			return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + "oracle" +File.separator + moduleName + File.separator + className + "Mapper.xml";
//		}

		if (template.contains("list.html.vm" )) {
			return "main" + File.separator + "resources" + File.separator + "templates" + File.separator
					+ "modules" + File.separator + moduleName + File.separator + className.toLowerCase() + ".html";
		}

		if (template.contains("list.js.vm" )) {
			return "main" + File.separator + "resources" + File.separator + "statics" + File.separator + "js" + File.separator
					+ "modules" + File.separator + moduleName + File.separator + className.toLowerCase() + ".js";
		}
//
		if (template.contains("menu.sql.vm" )) {
			return className.toLowerCase() + "_menu.sql";
		}
		return null;
	}
	/**
	 * 获取文件名
	 */
	public static String getFileNameByProjectDir(String template,String className, CodeModel codeParams) {

		if (template.contains("Entity.java.vm" ) && StringUtils.isNotBlank(codeParams.getEntityOutPath())) {
			File file = new File(codeParams.getEntityOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "DO.java";
		}

		if (template.contains("Dto.java.vm" ) && StringUtils.isNotBlank(codeParams.getDtoOutPath())) {
			File file = new File(codeParams.getDtoOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "DTO.java";
		}

		if (template.contains("Dao.java.vm" ) && StringUtils.isNotBlank(codeParams.getDaoOutPath())) {
			File file = new File(codeParams.getDaoOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "DAO.java";
		}

		if (template.contains("RpcService.java.vm" ) && StringUtils.isNotBlank(codeParams.getServiceOutPath())) {
			File file = new File(codeParams.getServiceOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + "Rpc" + className + "Service.java";
		}

		if (template.contains("Service.java.vm" ) && StringUtils.isNotBlank(codeParams.getServiceOutPath())) {
			File file = new File(codeParams.getServiceOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "Service.java";
		}

		if (template.contains("ServiceImpl.java.vm" ) && StringUtils.isNotBlank(codeParams.getServiceOutPath())) {
			File file = new File(codeParams.getServiceOutPath() + File.separator + "impl");
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "ServiceImpl.java";
		}

		if (template.contains("Bo.java.vm" ) && StringUtils.isNotBlank(codeParams.getServiceOutPath())) {
			File file = new File(codeParams.getBoOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "BO.java";
		}

		if (template.contains("BoImpl.java.vm" ) && StringUtils.isNotBlank(codeParams.getServiceOutPath())) {
			File file = new File(codeParams.getBoOutPath() + File.separator + "impl");
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "BOImpl.java";
		}

		if (template.contains("Controller.java.vm" ) && StringUtils.isNotBlank(codeParams.getControllerOutPath())) {
			File file = new File(codeParams.getControllerOutPath());
			if(!file.exists()){
				file.mkdirs();
			}
			return file.getPath() + File.separator + className + "Controller.java";
		}

        if (template.contains("MysqlDao.xml.vm" ) && StringUtils.isNotBlank(codeParams.getMapperOutPath())) {
            File file = new File(codeParams.getMapperOutPath() + File.separator + codeParams.getModuleName());
            if(!file.exists()){
				file.mkdirs();
			}
            return file.getPath() + File.separator + className + "Mapper.xml";
        }

//        if (template.contains("OracleDao.xml.vm" ) && StringUtils.isNotBlank(codeParams.getMapperOutPath())) {
//			File file = new File(codeParams.getMapperOutPath() + File.separator + "oracle" + File.separator + codeParams.getModuleName());
//            if(!file.exists()){
//				file.mkdirs();
//			}
//            return file.getPath() + File.separator + className + "Mapper.xml";
//        }

		if (template.contains("list.html.vm" ) && StringUtils.isNotBlank(codeParams.getHtmlOutPath())) {
			File file = new File(codeParams.getHtmlOutPath());
			if(!file.exists())file.mkdirs();
			return codeParams.getHtmlOutPath() + File.separator + className.toLowerCase() + ".html";
		}

		if (template.contains("list.js.vm" ) && StringUtils.isNotBlank(codeParams.getStaticOutPath())) {
			File file = new File(codeParams.getStaticOutPath());
			if(!file.exists())file.mkdirs();
			return codeParams.getStaticOutPath() + File.separator + className.toLowerCase() + ".js";
		}
//
		if (template.contains("menu.sql.vm" ) && StringUtils.isNotBlank(codeParams.getDaoOutPath())) {
			return codeParams.getDaoOutPath() + File.separator + className.toLowerCase() + "_menu.sql";
		}

		return null;
	}
}
